home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 9825 / 9825.xpi / chrome / content / qlauncher.js < prev    next >
Text File  |  2009-09-05  |  35KB  |  855 lines

  1. //Note to editor: NOT executed from XUL
  2. //Copyright 2009 Yongqian Li.
  3. (function()
  4. {
  5.     //var $ = smarterwiki_$;
  6.     //var $ = SW_$;
  7.     
  8.  
  9.     var add_this_shortcut = window.add_this_shortcut || "add this shortcut";
  10.     var delete_this_shortcut = window.delete_this_shortcut || "delete this shortcut";
  11.     var press_space_to_search = window.press_space_to_search || "press space to search";
  12.     var confirm_bookmark_delete = window.confirm_bookmark_delete || "Are you sure you want to delete this bookmark?";
  13.  
  14.     
  15.     function qlauncher(doc)//, invoke_key)
  16.     {
  17.         var DOM_VK_CANCEL = 3;
  18.         var DOM_VK_HELP = 6;
  19.         var DOM_VK_BACK_SPACE = 8;
  20.         var DOM_VK_TAB = 9;
  21.         var DOM_VK_CLEAR = 12;
  22.         var DOM_VK_RETURN = 13;
  23.         var DOM_VK_ENTER = 14;
  24.         var DOM_VK_SHIFT = 16;
  25.         var DOM_VK_CONTROL = 17;
  26.         var DOM_VK_ALT = 18;
  27.         var DOM_VK_PAUSE = 19;
  28.         var DOM_VK_CAPS_LOCK = 20;
  29.         var DOM_VK_ESCAPE = 27;
  30.         var DOM_VK_SPACE = 32;
  31.         var DOM_VK_PAGE_UP = 33;
  32.         var DOM_VK_PAGE_DOWN = 34;
  33.         var DOM_VK_END = 35;
  34.         var DOM_VK_HOME = 36;
  35.         var DOM_VK_LEFT = 37;
  36.         var DOM_VK_UP = 38;
  37.         var DOM_VK_RIGHT = 39;
  38.         var DOM_VK_DOWN = 40;
  39.         var DOM_VK_PRINTSCREEN = 44;
  40.         var DOM_VK_INSERT = 45;
  41.         var DOM_VK_DELETE = 46;
  42.         var DOM_VK_0 = 48;
  43.         var DOM_VK_1 = 49;
  44.         var DOM_VK_2 = 50;
  45.         var DOM_VK_3 = 51;
  46.         var DOM_VK_4 = 52;
  47.         var DOM_VK_5 = 53;
  48.         var DOM_VK_6 = 54;
  49.         var DOM_VK_7 = 55;
  50.         var DOM_VK_8 = 56;
  51.         var DOM_VK_9 = 57;
  52.         var DOM_VK_SEMICOLON = 59;
  53.         var DOM_VK_EQUALS = 61;
  54.         var DOM_VK_A = 65;
  55.         var DOM_VK_B = 66;
  56.         var DOM_VK_C = 67;
  57.         var DOM_VK_D = 68;
  58.         var DOM_VK_E = 69;
  59.         var DOM_VK_F = 70;
  60.         var DOM_VK_G = 71;
  61.         var DOM_VK_H = 72;
  62.         var DOM_VK_I = 73;
  63.         var DOM_VK_J = 74;
  64.         var DOM_VK_K = 75;
  65.         var DOM_VK_L = 76;
  66.         var DOM_VK_M = 77;
  67.         var DOM_VK_N = 78;
  68.         var DOM_VK_O = 79;
  69.         var DOM_VK_P = 80;
  70.         var DOM_VK_Q = 81;
  71.         var DOM_VK_R = 82;
  72.         var DOM_VK_S = 83;
  73.         var DOM_VK_T = 84;
  74.         var DOM_VK_U = 85;
  75.         var DOM_VK_V = 86;
  76.         var DOM_VK_W = 87;
  77.         var DOM_VK_X = 88;
  78.         var DOM_VK_Y = 89;
  79.         var DOM_VK_Z = 90;
  80.         var DOM_VK_CONTEXT_MENU = 93;
  81.         var DOM_VK_NUMPAD0 = 96;
  82.         var DOM_VK_NUMPAD1 = 97;
  83.         var DOM_VK_NUMPAD2 = 98;
  84.         var DOM_VK_NUMPAD3 = 99;
  85.         var DOM_VK_NUMPAD4 = 100;
  86.         var DOM_VK_NUMPAD5 = 101;
  87.         var DOM_VK_NUMPAD6 = 102;
  88.         var DOM_VK_NUMPAD7 = 103;
  89.         var DOM_VK_NUMPAD8 = 104;
  90.         var DOM_VK_NUMPAD9 = 105;
  91.         var DOM_VK_MULTIPLY = 106;
  92.         var DOM_VK_ADD = 107;
  93.         var DOM_VK_SEPARATOR = 108;
  94.         var DOM_VK_SUBTRACT = 109;
  95.         var DOM_VK_DECIMAL = 110;
  96.         var DOM_VK_DIVIDE = 111;
  97.         var DOM_VK_F1 = 112;
  98.         var DOM_VK_F2 = 113;
  99.         var DOM_VK_F3 = 114;
  100.         var DOM_VK_F4 = 115;
  101.         var DOM_VK_F5 = 116;
  102.         var DOM_VK_F6 = 117;
  103.         var DOM_VK_F7 = 118;
  104.         var DOM_VK_F8 = 119;
  105.         var DOM_VK_F9 = 120;
  106.         var DOM_VK_F10 = 121;
  107.         var DOM_VK_F11 = 122;
  108.         var DOM_VK_F12 = 123;
  109.         var DOM_VK_F13 = 124;
  110.         var DOM_VK_F14 = 125;
  111.         var DOM_VK_F15 = 126;
  112.         var DOM_VK_F16 = 127;
  113.         var DOM_VK_F17 = 128;
  114.         var DOM_VK_F18 = 129;
  115.         var DOM_VK_F19 = 130;
  116.         var DOM_VK_F20 = 131;
  117.         var DOM_VK_F21 = 132;
  118.         var DOM_VK_F22 = 133;
  119.         var DOM_VK_F23 = 134;
  120.         var DOM_VK_F24 = 135;
  121.         var DOM_VK_NUM_LOCK = 144;
  122.         var DOM_VK_SCROLL_LOCK = 145;
  123.         var DOM_VK_COMMA = 188;
  124.         var DOM_VK_PERIOD = 190;
  125.         var DOM_VK_SLASH = 191;
  126.         var DOM_VK_BACK_QUOTE = 192;
  127.         var DOM_VK_OPEN_BRACKET = 219;
  128.         var DOM_VK_BACK_SLASH = 220;
  129.         var DOM_VK_CLOSE_BRACKET = 221;
  130.         var DOM_VK_QUOTE = 222;
  131.         var DOM_VK_META = 224;
  132.  
  133.  
  134.         var keycodes = {};
  135.         keycodes[DOM_VK_SPACE] = ' ';
  136.         keycodes[DOM_VK_A] = 'a';
  137.         keycodes[DOM_VK_B] = 'b';
  138.         keycodes[DOM_VK_C] = 'c';
  139.         keycodes[DOM_VK_D] = 'd';
  140.         keycodes[DOM_VK_E] = 'e';
  141.         keycodes[DOM_VK_F] = 'f';
  142.         keycodes[DOM_VK_G] = 'g';
  143.         keycodes[DOM_VK_H] = 'h';
  144.         keycodes[DOM_VK_I] = 'i';
  145.         keycodes[DOM_VK_J] = 'j';
  146.         keycodes[DOM_VK_K] = 'k';
  147.         keycodes[DOM_VK_L] = 'l';
  148.         keycodes[DOM_VK_M] = 'm';
  149.         keycodes[DOM_VK_N] = 'n';
  150.         keycodes[DOM_VK_O] = 'o';
  151.         keycodes[DOM_VK_P] = 'p';
  152.         keycodes[DOM_VK_Q] = 'q';
  153.         keycodes[DOM_VK_R] = 'r';
  154.         keycodes[DOM_VK_S] = 's';
  155.         keycodes[DOM_VK_T] = 't';
  156.         keycodes[DOM_VK_U] = 'u';
  157.         keycodes[DOM_VK_V] = 'v';
  158.         keycodes[DOM_VK_W] = 'w';
  159.         keycodes[DOM_VK_X] = 'x';
  160.         keycodes[DOM_VK_Y] = 'y';
  161.         keycodes[DOM_VK_Z] = 'z';
  162.         keycodes[DOM_VK_SUBTRACT] = '-';
  163.  
  164.  
  165.         keycodes[DOM_VK_F1] = "F1";
  166.         keycodes[DOM_VK_F2] = "F2";
  167.         keycodes[DOM_VK_F3] = "F3";
  168.         keycodes[DOM_VK_F4] = "F4";
  169.         keycodes[DOM_VK_F5] = "F5";
  170.         keycodes[DOM_VK_F6] = "F6";
  171.         keycodes[DOM_VK_F7] = "F7";
  172.         keycodes[DOM_VK_F8] = "F8";
  173.         keycodes[DOM_VK_F9] = "F9";
  174.         keycodes[DOM_VK_F10] = "F10";
  175.         
  176.         keycodes[DOM_VK_ESCAPE] = "esc";
  177.  
  178.         
  179.         var USER_SEARCH_KEY_HINT = press_space_to_search;
  180.         var qlauncher_xhtml = '' +
  181.             '<div id="qlauncher">' +
  182.             '    <div id="qlauncher-overlay" style="display: none !important;">' +
  183.             '        <div id="qlauncher-floater">' +
  184.             '            <div id="qlauncher-panel">' +
  185.             '                <div id="qlauncher-search">' +
  186.             '                    <form id="qlauncher-search-form" action="#">' +
  187.             '                        <label for="qlauncher-search-input" class="qlauncher-shortcut-key">_: </label><div class="qlauncher-shortcut-favicon">' + 
  188.             '                            <a id="qlauncher-search-on-goog-link" class="qlauncher-shortcut-link" title="search on Google" href="http://google.com">' + 
  189.             '                                <img class="qlauncher-favicon" src="http://www.google.com/favicon.ico" alt=" " />' + 
  190.             '                            </a>' + 
  191.             '                        </div><input id="qlauncher-search-input" type="text" value="' + USER_SEARCH_KEY_HINT + '" spellcheck="true" />' +
  192.           //'                        <!--' +
  193.           //'                        NOTE: the reason that the above <>s are all scrunched up is that they' +
  194.           //'                        NOTE: are inline-block elements. HTML treats the newlines and' +
  195.           //'                        NOTE: spacing as spaces and allocates space for them, causing' +
  196.           //'                        NOTE: an extra gap' +
  197.           //'                        -->' +
  198.             '                    </form>' +
  199.             '                </div>' +
  200.             '                <div id="qlauncher-search-results">' +
  201.             '                    <ul id="qlauncher-search-result-list">' +
  202.             '                        <!--' +
  203.             '                        <li class="qlauncher-search-result">' +
  204.             '                        </li>' +
  205.             '                        -->' +
  206.             '                    </ul>' +
  207.             '                </div>' +
  208.             '                <div id="qlauncher-shortcuts">' +
  209.             '                    <ul id="qlauncher-shortcut-list" class="qlauncher-clearfix">' +
  210.             '                        <!--' +
  211.             '                        <li class="qlauncher-shortcut">' +
  212.             '                        </li>' +
  213.             '                        -->' +
  214.             '                    </ul>' +
  215.             '                </div>' +
  216.             '            </div>' +
  217.             '        </div>' +
  218.             '    </div>' +
  219.             '</div>';
  220.  
  221.         var searching = false;
  222.  
  223.         var injectQLauncher = function()
  224.         {
  225.             if($("#qlauncher", doc).size() == 0)
  226.             {
  227.                 $(qlauncher_xhtml, doc).appendTo($("body", doc));
  228.  
  229.                 $("#qlauncher-overlay", doc).mousedown(function(event)
  230.                 {
  231.                     if(is_qlauncher_active())
  232.                     {
  233.                         $("#qlauncher-overlay", doc).fadeOut("slow");
  234.                     }
  235.                 });
  236.                 $("#qlauncher-panel", doc).mousedown(function(event)
  237.                 {
  238.                     event.stopPropagation();//override above handler
  239.                     //don't cancel default or input would stop focusing.
  240.                 });
  241.  
  242.                 
  243.                 var currQuery = null;
  244.                 var searchTimer = null;
  245.                 var request = null;
  246.  
  247.                 var onactiviateSearch = function()
  248.                 {
  249.                     if(!searching)
  250.                     {
  251.                         searching = true;
  252.                         $("#qlauncher-shortcut-list", doc).slideUp("fast", function()
  253.                         {
  254.                             $("#qlauncher-search-result-list").slideDown("fast");
  255.                             //LOG("slid down: " + getComputedStyle(document.getElementById("qlauncher-search-result-list"), "").height);
  256.                         });
  257.  
  258.                         if($("#qlauncher-search-input", doc).attr("value") 
  259.                             == USER_SEARCH_KEY_HINT)
  260.                         {
  261.                             $("#qlauncher-search-input", doc).attr("value", "");
  262.                         }
  263.                     }
  264.                 };
  265.                 
  266.                 var ondeactivateSearch = function()
  267.                 {
  268.                     if(searching)
  269.                     {
  270.                         searching = false;
  271.                         $("#qlauncher-search-result-list").slideUp("fast", function()
  272.                         {
  273.                             $("#qlauncher-shortcut-list", doc).slideDown("fast");
  274.                             //LOG("slid up: " + getComputedStyle(document.getElementById("qlauncher-search-result-list"), "").height);
  275.                         });
  276.                         /*
  277.                         */
  278.                     }
  279.                 };
  280.                 
  281.                 var resetSearchTimer = function()
  282.                 {
  283.                     window.clearTimeout(searchTimer);
  284.                     searchTimer = null;
  285.                 };
  286.  
  287.                 var startSearchTimer = function()
  288.                 {
  289.                     searchTimer = window.setTimeout(function()
  290.                     {
  291.                         runSearch($("#qlauncher-search-input", doc).attr("value"))
  292.                     }, 450);
  293.                 };
  294.                 
  295.                 var addSearchResult = function(i, title, link_href, desc)
  296.                 {
  297.                     var shortcut_key = "F" + (i+1);
  298.                     var domain = new RegExp("http(?:s)?://([^/]*).*").exec(link_href)[1];
  299.                     var favicon_src = "http://" + domain + "/favicon.ico";
  300.  
  301.                     var $a_title = $('<a class="qlauncher-search-result-title"></a>')
  302.                                 .attr("href", link_href)
  303.                                 .append(title)
  304.  
  305.                     var $li = $('<li class="qlauncher-search-result-list-item"></li>').append(
  306.                         $('<div class="qlauncher-search-shortcut"></div>').append(
  307.                             '<div class="qlauncher-shortcut-key">' + 
  308.                             '    <a class="qlauncher-shortcut-link" href="' + link_href + '">' + 
  309.                             '        ' + shortcut_key + ': ' + 
  310.                             '    </a>' + 
  311.                             '</div>' + 
  312.                             '<div class="qlauncher-shortcut-favicon">' + 
  313.                             '    <a class="qlauncher-shortcut-link" href="' + link_href + '">' + 
  314.                             '        <img class="qlauncher-favicon" src="' + favicon_src + '" alt=" " />' + 
  315.                             '    </a>' + 
  316.                             '</div>'
  317.                         )
  318.                     ).append(
  319.                         $('<div class="qlauncher-search-result"></div>').append(
  320.                             $a_title
  321.                         ).append(
  322.                             $('<div class="qlauncher-search-result-desc"></div>')
  323.                                 .append(desc)
  324.                         )
  325.                     );
  326.  
  327.                     $("#qlauncher-search-result-list", doc).append(
  328.                         $li
  329.                     );
  330.  
  331.                     var command_handler = function()
  332.                     {
  333.                         $li
  334.                             .animate({opacity: "0"}, 200)
  335.                             .animate({opacity: "1"}, 200)
  336.                             .animate({opacity: "0"}, 200)
  337.                             .animate({opacity: "1"}, 200, "linear", function()
  338.                             {
  339.                                 if(SW_getBoolPref("extensions.smarterwiki.qlauncher_open_new_tab"))
  340.                                 {
  341.                                     SW_openNewTab(link_href);
  342.                                 }
  343.                                 else
  344.                                 {
  345.                                     doc.defaultView.location.href = link_href;
  346.                                 }
  347.                                 //$("#qlauncher-overlay", doc).fadeOut("fast");
  348.                             });
  349.                         return false;//cancel default actions associated with the function keys
  350.                     };
  351.  
  352.                     shortcut_search_command_of[shortcut_key] = command_handler;
  353.                     $a_title.click(command_handler);
  354.                     $li.click(command_handler);
  355.                 };
  356.                 
  357.                 var getGoogleSearchURL = function(query)
  358.                 {
  359.                     return "http://www.google.com/search?q=" + encodeURIComponent(query)
  360.                 }
  361.  
  362.                 var runSearch = function(query)
  363.                 {
  364.                     if(query)
  365.                     {
  366.                         $("#qlauncher-search", doc).addClass("searching");
  367.                         request = SW_$getJSON("http://ajax.googleapis.com/ajax/services/search/web",//safe because this code is sandboxed
  368.                             {
  369.                                 "v": "1.0",
  370.                                 "rsz": "small",
  371.                                 "q": query,
  372.                             },
  373.                             function(data)
  374.                             {
  375.                                 $("#qlauncher-search", doc).removeClass("searching");
  376.                                 $("#qlauncher-search-result-list", doc).slideUp("fast", function()
  377.                                 {
  378.                                     $("#qlauncher-search-result-list", doc).empty().hide();
  379.  
  380.                                     for(var i = 0; i < data["responseData"]["results"].length; i++)
  381.                                     {
  382.                                         addSearchResult(i, data["responseData"]["results"][i]["title"], 
  383.                                             data["responseData"]["results"][i]["unescapedUrl"], 
  384.                                             data["responseData"]["results"][i]["content"]);
  385.                                     }
  386.                                     addSearchResult(data["responseData"]["results"].length,
  387.                                         "More results...", 
  388.                                         getGoogleSearchURL(query),
  389.                                         "");
  390.                                     $("#qlauncher-search-result-list", doc).slideDown("fast");
  391.                                 });
  392.                             }
  393.                         );
  394.                     }
  395.                 };        
  396.  
  397.                 $("form#qlauncher-search").submit(function(e)
  398.                 {
  399.                     e.preventDefault();
  400.                     resetSearchTimer();
  401.                     runSearch($("#qlauncher-search-input").attr("value"));
  402.                 });
  403.  
  404.                 var inputChanged = function()
  405.                 {
  406.                     if(this.value == currQuery)
  407.                     {
  408.                         return;
  409.                     }
  410.                     currQuery = this.value;
  411.                     $("#qlauncher-search-on-goog-link").attr("href", 
  412.                         getGoogleSearchURL(this.value));
  413.                     if(request)
  414.                     {
  415.                         SW_abortReq(request);//request.abort();
  416.                         request = null;
  417.                         $("#qlauncher-search", doc).removeClass("searching");
  418.                     }
  419.                     resetSearchTimer();
  420.                     startSearchTimer();
  421.                 };
  422.                 
  423.                 $("#qlauncher-search-input", doc)
  424.                     .focus(inputChanged)
  425.                     .change(inputChanged)
  426.                     .keypress(inputChanged)
  427.                     .keydown(inputChanged)
  428.                     .keyup(inputChanged)
  429.                     //.mouseup(inputChanged)
  430.                     //.mousedown(inputChanged)
  431.                     //.mouseout(inputChanged)
  432.                     //.click(inputChanged)
  433.                     .blur(inputChanged);
  434.                 $("#qlauncher-search-input", doc).focus(function()
  435.                 {
  436.                     onactiviateSearch();
  437.                 });
  438.                 /*
  439.                 $("#qlauncher-search-input", doc).blur(function()
  440.                 {
  441.                     ondeactivateSearch();
  442.                 });
  443.                 */
  444.  
  445.                 $("#qlauncher-panel", doc).click(function()
  446.                 {
  447.                     ondeactivateSearch();
  448.                 });
  449.  
  450.                 $("#qlauncher-search-input", doc)
  451.                     .click(function(event)
  452.                     {
  453.                         event.stopPropagation();//override above handler
  454.                     });            
  455.             }
  456.             //else ignore
  457.         };
  458.  
  459.  
  460.  
  461.         var is_qlauncher_active = function()
  462.         {
  463.             var d = $("#qlauncher-overlay", doc).css("display");
  464.             return d != "none" && d != null;
  465.         }
  466.         
  467.         var is_qlauncher_enabled = function()
  468.         {
  469.             return SW_getBoolPref("extensions.smarterwiki.enable_qlauncher");
  470.         }
  471.             
  472.  
  473.  
  474.         var shortcut_command_of = {};
  475.         var shortcut_search_command_of = {};
  476.  
  477.         var addShortcut = function(shortcut_key, favicon_src, label, link_href, itemId, phantom)
  478.         {
  479.             var colored_label = "";
  480.             var already_colored = false;
  481.             for(var c = 0; c < label.length; c++)
  482.             {
  483.                 if(label.charAt(c).toLowerCase() == shortcut_key && !already_colored)
  484.                 {
  485.                     colored_label += '<span class="qlauncher-key-char">';
  486.                     colored_label += label.charAt(c);
  487.                     colored_label += '</span>';
  488.                     already_colored = true;
  489.                 }
  490.                 else
  491.                 {
  492.                     colored_label += label.charAt(c);
  493.                 }
  494.             }
  495.  
  496.             var $shortcut = $(
  497.                 '<div class="qlauncher-shortcut-key">' + 
  498.                 '    <a class="qlauncher-shortcut-link" href="' + link_href + '">' + 
  499.                 '        ' + shortcut_key + ': ' + 
  500.                 '    </a>' + 
  501.                 '</div>' + 
  502.                 '<div class="qlauncher-shortcut-favicon">' + 
  503.                 '    <a class="qlauncher-shortcut-link" href="' + link_href + '">' + 
  504.                 '        <img class="qlauncher-favicon" src="' + favicon_src + '" alt=" " />' + 
  505.                 '    </a>' + 
  506.                 '</div>' + 
  507.                 '<div class="qlauncher-shortcut-label">' + 
  508.                 '    <a class="qlauncher-shortcut-link qlauncher-text" href="' + link_href + '">' + 
  509.                 '        ' + colored_label + 
  510.                 '    </a>' + 
  511.                 '</div>'
  512.             , doc);
  513.  
  514.             var $li = $('<li class="qlauncher-shortcut-list-item"></li>', doc);
  515.             var $enclosing_div = $('<div class="qlauncher-shortcut"></div>', doc)
  516.                 .append($shortcut)
  517.                 .appendTo(
  518.                     $li.appendTo($("#qlauncher-shortcut-list", doc))
  519.                 );
  520.  
  521.             if(phantom)
  522.             {
  523.                 $enclosing_div.addClass("phantom");
  524.                 var $add_button = $('<a href="#" title="' + add_this_shortcut + '" class="qlauncher-add-shortcut-button"></a>')
  525.                     .click(function(event)
  526.                     {
  527.                         $enclosing_div
  528.                             .animate({opacity: "0"}, 200)
  529.                             .animate({opacity: "1"}, 200)
  530.                             .animate({opacity: "0"}, 200)
  531.                             .animate({opacity: "1"}, 200, "linear", function()
  532.                             {
  533.                                 SW_addShortcut(label, link_href);
  534.                                 setupShortcutList();
  535.                                 SW_showBookmarkCurrentPageEdit();
  536.                             });
  537.                         return false;
  538.                     })
  539.                     .appendTo($enclosing_div);
  540.             }
  541.             else
  542.             {            
  543.                 var $delete_button = $('<a href="#" title="' + delete_this_shortcut + '" class="qlauncher-delete-shortcut-button"></a>')
  544.                     .click(function(event)
  545.                     {
  546.                         if(doc.defaultView.confirm(confirm_bookmark_delete))
  547.                         { 
  548.                             SW_removeBookmark(itemId);
  549.                             $li.fadeOut("fast", function()
  550.                                 {
  551.                                     $li.remove();
  552.                                 });
  553.                         }
  554.                         return false;
  555.                     })
  556.                     .appendTo($enclosing_div);
  557.             }
  558.  
  559.             var command_handler = function()
  560.             {
  561.                 $enclosing_div
  562.                     .animate({opacity: "0"}, 200)
  563.                     .animate({opacity: "1"}, 200)
  564.                     .animate({opacity: "0"}, 200)
  565.                     .animate({opacity: "1"}, 200, "linear", function()
  566.                     {
  567.                         if(SW_getBoolPref("extensions.smarterwiki.qlauncher_open_new_tab"))
  568.                         {
  569.                             SW_openNewTab(link_href);
  570.                         }
  571.                         else
  572.                         {
  573.                             doc.defaultView.location.href = link_href;
  574.                         }
  575.                         $("#qlauncher-overlay", doc).fadeOut("fast");
  576.                     });
  577.             };
  578.  
  579.             shortcut_command_of[shortcut_key] = command_handler;
  580.  
  581.             $enclosing_div.click(function()
  582.             {
  583.                 command_handler();
  584.                 return false; //cancel link action
  585.             });
  586.         };    
  587.  
  588.  
  589.         var cleanupShortcuts = function(shortcuts)
  590.         {
  591.            var used_keys = {};
  592.            for(var i = 0; i < shortcuts.length; i++)
  593.            {
  594.                if(shortcuts[i][0]) {
  595.                    used_keys[shortcuts[i][0]] = true;
  596.                }
  597.            }
  598.            var letters = "abcdefghijklmnopqrstuvwxyz";
  599.            for(var i = 0; i < shortcuts.length; i++)
  600.            {
  601.                if(!shortcuts[i][0]) 
  602.                {
  603.                    var try_letters = shortcuts[i][2].toLowerCase() + letters;
  604.                    for(var b = 0; b < try_letters.length; b++)
  605.                    {
  606.                        var c = try_letters.charAt(b);
  607.                        if(c != " " && !used_keys[c])
  608.                        {
  609.                            shortcuts[i][0] = c;
  610.                            used_keys[c] = true;
  611.                            break;
  612.                        }
  613.                    }
  614.                }
  615.            }
  616.         };
  617.  
  618.         var addShortcuts = function(shortcuts)
  619.         {
  620.             cleanupShortcuts(shortcuts);
  621.             for(var i = 0; i < shortcuts.length; i++) //for each(var shortcut in shortcuts) //YUI chokes
  622.             {
  623.                 var shortcut = shortcuts[i];
  624.                 //addShortcut.apply(this, shortcut);
  625.                 addShortcut(shortcut[0], shortcut[1], shortcut[2], shortcut[3], shortcut[4], false);
  626.                 //(shortcut_key, favicon_src, label, link_href, itemId, phantom)
  627.             }
  628.             addShortcut("?", SW_getFaviconURL(document.URL), document.title, document.URL, -1, true);
  629.             /*
  630.             addShortcut("a", "http://amazon.com/favicon.ico", "Amazon", "http://amazon.com");
  631.             addShortcut("d", "http://digg.com/favicon.ico", "Digg", "http://digg.com");
  632.             addShortcut("f", "http://facebook.com/favicon.ico", "Facebook", "http://facebook.com");
  633.             addShortcut("g", "http://google.com/favicon.ico", "Google", "http://google.com");
  634.             addShortcut("l", "http://lifehacker.com/favicon.ico", "Lifehacker", "http://lifehacker.com");
  635.             addShortcut("m", "http://mashable.com/favicon.ico", "Mashable", "http://mashable.com");
  636.             addShortcut("n", "http://nytimes.com/favicon.ico", "NYTimes", "http://nytimes.com");
  637.             addShortcut("r", "http://www.google.com/reader/ui/favicon.ico", "Reader", "http://reader.google.com");
  638.             addShortcut("s", "http://myspace.com/favicon.ico", "MySpace", "http://myspace.com");
  639.             addShortcut("u", "http://youtube.com/favicon.ico", "YouTube", "http://youtube.com");
  640.             addShortcut("w", "http://en.wikipedia.org/favicon.ico", "Wikipedia", "http://en.wikipedia.org");
  641.             addShortcut("x", "http://readwriteweb.com/favicon.ico", "ReadWriteWeb", "http://readwrtieweb.com");
  642.             //addShortcut("y", "http://yahoo.com/favicon.ico", "Yahoo", "http://yahoo.com");
  643.             */
  644.         };
  645.         
  646.         /*
  647.         var deepArrayEquals = function(a1, a2)
  648.         {
  649.             if(a1 == null || a2 == null) {
  650.                 return a1 == a2;
  651.             }
  652.             if(a1.length == a2.length) {
  653.                 for(var i = 0; i < a1.length; i++) {
  654.                     if(a1[i] instanceof Array && a2[i] instanceof Array) {
  655.                         if(!deepArrayEquals(a1[i], a2[i])) {
  656.                             // alert("deep a1[i] != a2[i]" + a1[i] + " " + a2[i]);
  657.                             return false;
  658.                         }
  659.                     }
  660.                     else if(a1[i] != a2[i]) {
  661.                         // alert("" + (typeof a1[i]) + (a1[i] instanceof Array) + " " + (a2[i] instanceof Array) + " a1[i] != a2[i]" + a1[i] + " " + a2[i]);
  662.                         return false;
  663.                     }
  664.                 }
  665.                 return true;
  666.             }
  667.             return false;
  668.         }; // alert(deepArrayEquals(null, null) && deepArrayEquals([1, 2, 3], [1, 2, 3]) && !deepArrayEquals([1, 2, 3], [1, 2, 4]) && deepArrayEquals([1, 2, "3"], [1, 2, "3"]) && deepArrayEquals([[1, 2], 2, ["3", 0]], [[1, 2], 2, ["3", 0]]));
  669.         */
  670.  
  671.  
  672.         var current_shortcuts = null;
  673.         var setupShortcutList = function()
  674.         {
  675.             if(doc.designMode == "off")
  676.             {
  677.                 if($("#qlauncher", doc).size() == 0)
  678.                 {   
  679.                     injectQLauncher();
  680.                 }
  681.  
  682.                 var user_shortcuts = SW_getShortcuts();
  683.  
  684.                 if(current_shortcuts == null || current_shortcuts.toString() != user_shortcuts.toString()) //toString() is a bit of a hack but those are not Arrays and thus deepArrayEquals doesn't work
  685.                 {
  686.                     //alert("emptying");
  687.                     $("#qlauncher-shortcut-list", doc).empty();
  688.                     addShortcuts(user_shortcuts);
  689.                     current_shortcuts = user_shortcuts;
  690.                 }
  691.                 
  692.                 return true;
  693.             }
  694.             else
  695.             {
  696.                 return false;
  697.             }
  698.         };
  699.  
  700.         //preload qLauncher for speed
  701.         if(is_qlauncher_enabled())
  702.         {
  703.             if(doc.location.href.indexOf("https://") != 0 && SW_hasShortcuts()) // prevent (or delay) "Connection Partially Encrypted" warnings and do not create qLauncher folder
  704.             {
  705.                 setTimeout(function()
  706.                 {
  707.                     setupShortcutList();
  708.                 }, 2000); //only preload after 2 secs
  709.             }
  710.         }
  711.         /* //do not pre-load -- can cause problems with html and introduce too much load on servers
  712.         */
  713.         
  714.         var getInvokeKey = function()
  715.         {
  716.             var invoke_key = SW_getCharPref(
  717.                 "extensions.smarterwiki.qlauncher_invoke_key");
  718.             return invoke_key;
  719.         };
  720.  
  721.         var matchesInvokeKey = function(event)
  722.         {
  723.             var keys = getInvokeKey().match(/(META|CTRL|ALT|SHIFT|[^+]+)/g);
  724.             var c = "";
  725.             if(event.keyCode != 0)
  726.             {
  727.                 if(event.keyCode in keycodes) {
  728.                     c = keycodes[event.keyCode];
  729.                 }
  730.             }
  731.             else
  732.             {
  733.                c = String.fromCharCode(event.charCode); 
  734.             }
  735.             if(c == " ") {
  736.                 c = "SPACE";
  737.             }
  738.             c = c.toUpperCase();
  739.             
  740.             var expected_c = "";
  741.             for(var i = 0; i < keys.length; i++)
  742.             {
  743.                 var key = keys[i];
  744.                 if(!key.match(/(META|CTRL|ALT|SHIFT)/)) {
  745.                     expected_c = key;
  746.                     break;
  747.                 }
  748.             }
  749.  
  750.             return (event.ctrlKey == (keys.indexOf("CTRL") != -1)) && 
  751.                 (event.altKey == (keys.indexOf("ALT") != -1)) && 
  752.                 (event.shiftKey == (keys.indexOf("SHIFT") != -1)) && 
  753.                 (keys.indexOf("META") == -1 || event.metaKey) && //if meta key is specified and there is no meta key -> bad, else OK. This is because ctrl also counts as meta
  754.                 (c == expected_c);
  755.         };
  756.  
  757.         //doc.getElementsByTagName("body")[0].addEventListener("keydown", function(event)
  758.         //doc.wrappedJSObject.defaultView.addEventListener("keydown", function(event)
  759.         //window.addEventListener("keydown", function(event)
  760.         $(doc.defaultView).keydown(function(event)
  761.         {
  762.             if(matchesInvokeKey(event))
  763.             {
  764.                 if(is_qlauncher_active())
  765.                 {
  766.                     $("#qlauncher-overlay", doc).fadeOut("slow");
  767.                     //event.stopPropagation();
  768.                     //event.preventDefault();
  769.                     return false; //IMPORTANT: cancel it so that Firebug does not steal focus
  770.                 }
  771.                 else if(is_qlauncher_enabled())
  772.                 {
  773.                     if(setupShortcutList()) // a bit slower to setup on the fly but more flexible
  774.                     {
  775.                         $("#qlauncher-overlay", doc).fadeIn("fast");
  776.                         $("#qlauncher-panel", doc)[0].focus();
  777.                         //event.stopPropagation();
  778.                         //event.preventDefault();
  779.                         return false; //IMPORTANT: cancel it so that Firebug does not steal focus
  780.                     }
  781.                     else
  782.                     {
  783.                         //we didn't handle the even cuz of problem setting up, do *not* cancel event
  784.                     }
  785.                 }
  786.                 //not active and not enabled? do not eat event then
  787.             }
  788.             else
  789.             {
  790.                 if(is_qlauncher_active())
  791.                 {
  792.                     if(event.keyCode in keycodes)
  793.                     {
  794.                         if(!searching &&
  795.                             (keycodes[event.keyCode] == " " || keycodes[event.keyCode] == "-"))
  796.                         {
  797.                             setTimeout(function()
  798.                             {
  799.                                 $("#qlauncher-search-input", doc).focus();
  800.                             }, 50);//to prevent space from being typed into input
  801.                             return false; //to prevent page from moving down
  802.                         }    
  803.  
  804.                         if(keycodes[event.keyCode] == "esc")
  805.                         {
  806.                             $("#qlauncher-overlay", doc).fadeOut("slow");
  807.                         }
  808.  
  809.                         if(!searching && keycodes[event.keyCode] in shortcut_command_of)
  810.                         {
  811.                             return shortcut_command_of[keycodes[event.keyCode]]();
  812.                         }
  813.  
  814.                         if(searching && keycodes[event.keyCode] in shortcut_search_command_of)
  815.                         {
  816.                             return shortcut_search_command_of[keycodes[event.keyCode]]();
  817.                         }
  818.                     }
  819.                 }
  820.             }
  821.         });
  822.         //}, false);
  823.         
  824.         $(doc.defaultView).keyup(function(event)
  825.         {
  826.             if(is_qlauncher_active() && !searching)
  827.             {
  828.                 /*
  829.                 var c = String.fromCharCode(event.which);
  830.                 if(c == "-" || c == "_" || c == " ")
  831.                 {
  832.                 */
  833.             }        
  834.         });
  835.     };
  836.  
  837.     $(document).ready(function()
  838.     {
  839.         if(document.designMode == "off")
  840.         {
  841.             if(self === top) //do not execute in iframe
  842.             {
  843.                 qlauncher(document);//, "CTRL+SPACE";
  844.             }
  845.             else
  846.             {
  847.                 // alert("in an iframe");
  848.             }
  849.         }
  850.         else
  851.         {
  852.             //alert("in rich editor");
  853.         }
  854.     });
  855. }());